﻿using Furion.EventBus;

namespace GoodAdmin.System;

/// <inheritdoc cref="ISysRoleService"/>
public class SysRoleService : DbRepository<SysRole>, ISysRoleService, ITransient
{
    private readonly IRelationService _relationService;
    private readonly IRedisCacheManager _redisManager;
    private readonly IMenuService _menuService;
    private readonly IEventPublisher _eventPublisher;

    public SysRoleService(IRelationService relationService, IRedisCacheManager redisManager,IMenuService menuService, IEventPublisher eventPublisher)
    {
        _relationService = relationService;
        _redisManager = redisManager;
        _menuService = menuService;
        _eventPublisher = eventPublisher;
    }

    /// <inheritdoc/>
    public async Task AddRole(RoleAddInput input)
    {
        await CheckInput(input,CommonConst.ACTION_INSERT);
        var role = input.Adapt<SysRole>();
        //默认为有效
        role.Status = input.Status ?? CommonConst.STATUS_ENABLE;
        //数据范围默认为本部门
        role.DataScope = input.DataScope ?? RoleConst.DATA_SCOPE_OWNORG;
        if(await InsertAsync(role))
        {
            //删除缓存
            RefreshCahce();
        }            
    }

    /// <inheritdoc/>
    public async Task UpdateRole(RoleUpdateInput input)
    {        
        await CheckInput(input,CommonConst.ACTION_UPDATE);      
        var role = input.Adapt<SysRole>();
        if(await UpdateAsync(role))
        {
            //删除缓存
            RefreshCahce();
            await RefreshUserCache(role.Id);
        }
    }

    /// <inheritdoc/>
    public async Task DeleteRole(List<BaseIdInput> input)
    {
        var roleList = await GetRoleList();
        var ids = input.Select(i => i.Id).ToList();
        if(roleList.Any(it=>ids.Contains(it.Id) && it.SystemMark == CommonConst.SYSTEM))
        {
            throw Oops.Bah(RoleErrorCode.R004);
        }
        if(await IsAnyAsync(r=>r.SystemMark== CommonConst.SYSTEM && ids.Contains(r.Id)))
        {
            throw Oops.Bah(RoleErrorCode.R004);
        }
        var res = await Context.Ado.UseTranAsync(async () =>
        {
            //删除角色
            await DeleteByIdsAsync(ids.Cast<object>().ToArray());
            //删除角色菜单信息
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_MENU, ids);
            //删除角色用户信息
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_USER, ids);
            //删除角色资源信息
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_RESOURCE, ids);
        });
        if (res.IsSuccess)
        {
            //删除缓存
            RefreshCahce();           
            await RefreshUserCache(ids.ToArray());
        }            
    }

    /// <inheritdoc/>
    public async Task<List<SysRole>> GetUserRoleList(long userId)
    {
        var relationList = await _relationService.GetRelationListByTargetId(CategoryConst.RELATION_ROLE_USER, userId);
        var roleIdList = relationList.Select(r => r.ObjectId).ToList();
        if (roleIdList.Count == 0) return [];
        var roleList = await GetRoleList();        
        return roleList.Where(r => roleIdList.Contains(r.Id)).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<SysRole>> GetUserRoleListByUserOrgId(long orgId)
    {
        var relationList = await _relationService.GetRelationListByTargetId(CategoryConst.RELATION_ROLE_ORG, orgId);
        var roleIdList = relationList.Select(r => r.ObjectId).ToList();
        if (roleIdList.Count == 0) return [];
        var roleList = await GetRoleList();
        return roleList.Where(r => roleIdList.Contains(r.Id)).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<SysRole>> GetRoleList()
    {
        var key = CacheConst.SysRole;

        var roleList = _redisManager.Get<List<SysRole>>(key);
        if(roleList== null)
        {
            roleList = await GetListAsync();
            _redisManager.Set(key, roleList);
        }
        return roleList;
    }

    /// <inheritdoc/>
    public async Task<SqlSugarPagedList<SysRole>> GetRolePage(RolePageInput input)
    {
        return await Context.Queryable<SysRole>()
            .WhereIF(!string.IsNullOrWhiteSpace(input.RoleName), r => r.RoleName.Contains(input.RoleName))
            .OrderByIF(!string.IsNullOrWhiteSpace(input.SortField), r => $"{input.SortField} {input.SortOrder}")
            .OrderBy(r=>r.SortCode)
            .ToPagedListAsync(input.PageIndex, input.PageSize);
    }
    /// <inheritdoc/>
    public async Task<List<RoleSelectorOutput>> GetRoleSelector(RolePageInput input)
    {
        var roleList = await GetRoleList();
        var res = roleList.WhereIF(!string.IsNullOrWhiteSpace(input.RoleName), r => r.RoleName.Contains(input.RoleName))
            .OrderBy(r => r.SortCode).Adapt<List<RoleSelectorOutput>>();
        return res;
    }  

    /// <inheritdoc/>
    public async Task<RoleResourceInfo> GetRoleOwnResource(BaseIdInput input)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_RESOURCE, input.Id);
        var roleResourceJson = relation.Select(it => it.ExtraJson).FirstOrDefault();
        var roleResource= roleResourceJson.ToJsonEntity<List<ResourceInfo>>();
        return new RoleResourceInfo { Id = input.Id, Resources = roleResource };
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetRoleOweMenu(BaseIdInput input)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_MENU, input.Id);
        return relation.Select(it => it.TargetId).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetRoleOwnUser(long roleId)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_USER, roleId);
        return relation.Select(it=>it.TargetId).ToList(); 
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetRoleOwnOrg(long roleId)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_ORG, roleId);
        return relation.Select(it => it.TargetId).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetRoleOwnPosition(long roleId)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_POSITION, roleId);
        return relation.Select(it => it.TargetId).ToList();
    }

    /// <inheritdoc/>
    public async Task GrantRoleMenus(RoleMenusInput input)
    {              
        var sysRole = (await GetRoleList()).Where(r => r.Id == input.Id).First();
        if (sysRole == null) return;
        if(sysRole.Code== RoleConst.SUPERADMIN)
        {
            throw Oops.Bah(RoleErrorCode.R013);
        }
        var res= await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_MENU, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_MENU, input.Id, input.MenuList);
        });
        
        if (res.IsSuccess)
        {

            await RefreshUserCache(input.Id);
            //如果当前角色为系统基础角色,那么需要移除CommonRoleMenu缓存           
            if (sysRole.Code == RoleConst.ROLE_COMMON)
                _redisManager.Remove(CacheConst.CommonRoleMenu);
        }
    }

    /// <inheritdoc/>
    public async Task GrantRoleUsers(RoleUsersInput input)
    {
        var sysRole = (await GetRoleList()).Where(r => r.Id == input.Id).First();
        if (sysRole == null) return;
        if (sysRole.Code == RoleConst.ROLE_COMMON)
        {
            throw Oops.Bah(RoleErrorCode.R015);
        }
        var userRoleRelationList = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_USER, input.Id);

        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_USER, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_USER, input.Id, input.UserList);
        });
        if (res.IsSuccess)
        {
            var userIds = userRoleRelationList.Select(it => it.TargetId).ToList();
            userIds.AddRange(input.UserList);
            //删除缓存
            await RefreshUserCacheByUserIds([..userIds]);
        }         
    }

    /// <inheritdoc/>
    public async Task GrantRoleOrgs(RoleOrgsInput input)
    {
        var sysRole = (await GetRoleList()).Where(r => r.Id == input.Id).First();
        if (sysRole == null) return;
        if (sysRole.Code == RoleConst.ROLE_COMMON)
        {
            throw Oops.Bah(RoleErrorCode.R016);
        }
        var orgRoleRelationList = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_ORG, input.Id);

        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_ORG, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_ORG, input.Id, input.OrgList);
        });
        if (res.IsSuccess)
        {
            var orgIds = orgRoleRelationList.Select(it => it.TargetId).ToList();
            orgIds.AddRange(input.OrgList);
            //删除缓存
            await RefreshUserCacheByOrgIds([..orgIds]);
        }
    }

    /// <inheritdoc/>
    public async Task GrantRolePositions(RolePositionsInput input)
    {
        var sysRole = (await GetRoleList()).Where(r => r.Id == input.Id).First();
        if (sysRole == null) return;
        if (sysRole.Code == RoleConst.ROLE_COMMON)
        {
            throw Oops.Bah(RoleErrorCode.R016);
        }
        var positionRoleRelationList = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_POSITION, input.Id);

        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_POSITION, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_POSITION, input.Id, input.PositionList);
        });
        if (res.IsSuccess)
        {
            var positionIds = positionRoleRelationList.Select(it => it.TargetId).ToList();
            positionIds.AddRange(input.PositionList);
            //删除缓存
            await RefreshUserCacheByPositionIds([.. positionIds]);
        }
    }

    /// <inheritdoc/>
    public async Task GrantRoleResource(RoleResourceInput input)
    {
        var roleList = await GetRoleList();
        var role = roleList.First(r => r.Id==input.Id);
        if (role.Code == RoleConst.SUPERADMIN)
        {
            throw Oops.Bah(RoleErrorCode.R014);
        }
        input.Resources.ForEach(it =>
        {            
            if (it.IsDataScope && string.IsNullOrWhiteSpace(it.DataScopeType))
            {
                throw Oops.Bah(RoleErrorCode.R006, it.ApiRoute);
            }
            if (role.Code == RoleConst.ROLE_COMMON && it.IsDataScope && it.DataScopeType!= RoleConst.DATA_SCOPE_SELF){
                throw Oops.Bah(RoleErrorCode.R012);
            }
            if ((it.DataScopeType == RoleConst.DATA_SCOPE_DEFINE || it.DataScopeType==RoleConst.DATA_SCOPE_APPOINT_ORG_CHILD ) 
                && it.DefineScopeOrgList.Count == 0 )
            {
                throw Oops.Bah(RoleErrorCode.R006, it.ApiRoute);
            }
        });
        var json = input.Resources.ToJson();
        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_RESOURCE, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_RESOURCE, input.Id, input.Id, json);
        });
        if(res.IsSuccess)
        {
            await RefreshUserCache( input.Id );
            if(role.Code == RoleConst.ROLE_COMMON)
            {
                RefreshCommonApiRole();
            }
        }                     
    }

    /// <inheritdoc/>
    public async Task<SysRole> GetCommonRole()
    {
        var roleList = await GetRoleList();
        var commonRole = roleList.First(r => r.Code == RoleConst.ROLE_COMMON);
        return commonRole;
    }

    /// <inheritdoc/>
    public async Task<List<MenuSimpleInfo>> GetCommonRoleMenu()
    {
        string key = CacheConst.CommonRoleMenu;
        var menuList = _redisManager.Get<List<MenuSimpleInfo>>(key);
        if (menuList == null)
        {
            //先获取基础权限
            var commonRole = await GetCommonRole();
            //根据权限Id
            menuList = await _menuService.GetMenuListByRoleId(commonRole.Id);
            _redisManager.Set(key, menuList);
        }
        return menuList;
    }

    /// <summary>
    /// 检查输入
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    private async Task CheckInput(SysRole input, string action)
    {
        if ((input.DataScope == RoleConst.DATA_SCOPE_DEFINE || input.DataScope==RoleConst.DATA_SCOPE_APPOINT_ORG_CHILD ) 
            && input.DefineScopeOrgList.Count==0)
        {
            throw Oops.Bah(RoleErrorCode.R006);
        }

        var roleList = await GetRoleList();
        //判断是否有同名角色
        if (roleList.Any(r => r.RoleName == input.RoleName && input.Id != r.Id))
        {
            throw Oops.Bah(RoleErrorCode.R001);
        }
        //判断是否有相同角色编码(忽略大小写)
        if (roleList.Any(r => r.Code.EqualIgnoreCase(input.Code) && input.Id != r.Id))
        {
            throw Oops.Bah(RoleErrorCode.R008);
        }
        if (action == CommonConst.ACTION_UPDATE)
        {
            var role = roleList.FirstOrDefault(r => r.Id == input.Id) ?? throw Oops.Bah(RoleErrorCode.R009);
            if (role.SystemMark == CommonConst.SYSTEM && (role.RoleName != input.RoleName || role.Code != input.Code))
            {
                throw Oops.Bah(RoleErrorCode.R009);
            }
        }
    }

    /// <summary>
    /// 清除角色缓存
    /// </summary>
    private void RefreshCahce()
    {
        _redisManager.Remove(CacheConst.SysRole);
    }
    /// <summary>
    /// 清除公共角色的接口权限
    /// </summary>
    private void RefreshCommonApiRole()
    {
        _redisManager.Remove(CacheConst.CommonRoleApi);
    }
    private async Task RefreshUserCache(params long[] roleIds)
    {
        if (roleIds.Length == 0) return;
        await _eventPublisher.PublishAsync(EventSubscriberConst.RemoveDataScopeByRoleIds, roleIds);
    }

    private async Task RefreshUserCacheByOrgIds(params long[] orgIds)
    {
        if (orgIds.Length == 0) return; 
        await _eventPublisher.PublishAsync(EventSubscriberConst.RemoveDataScopeByOrgIds, orgIds);
    }

    private async Task RefreshUserCacheByPositionIds(params long[] orgIds)
    {
        if (orgIds.Length == 0) return;
        await _eventPublisher.PublishAsync(EventSubscriberConst.RemoveDataScopeByPositionIds, orgIds);
    }

    private async Task RefreshUserCacheByUserIds(params long[] userIds)
    {
        if (userIds.Length == 0) return;
        await _eventPublisher.PublishAsync(EventSubscriberConst.RemoveDataScopeByUserIds, userIds);
    }
}